# -*- coding: utf-8 -*-

"""
游戏主界面组件
"""

import sys
import os
import random
import cv2
import numpy as np
from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, 
                             QApplication, QWidget, QVBoxLayout, QPushButton, 
                             QGraphicsOpacityEffect, QLabel, QHBoxLayout, QFileDialog)
from PyQt5.QtGui import QPixmap, QPainter, QPen, QColor, QBrush, QImage
from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal
import logging

# 配置日志
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

class PuzzlePiece(QGraphicsPixmapItem):
    def __init__(self, pixmap, current_pos, original_pos, parent=None):
        super().__init__(parent)  # 先调用父类初始化
        self.setPixmap(pixmap)  # 然后设置pixmap
        
        self.current_pos = current_pos
        self.original_pos = original_pos
        self.setFlag(QGraphicsPixmapItem.ItemIsMovable)
        self.setFlag(QGraphicsPixmapItem.ItemSendsGeometryChanges)
        self.setAcceptHoverEvents(True)
        self.dragged = False
        self.setZValue(0)
        self.setOpacity(1.0)
        self.setTransformationMode(Qt.SmoothTransformation)
        
        # 存储原始位置用于验证
        self.piece_width = pixmap.width()
        self.piece_height = pixmap.height()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragged = True
            self.setZValue(1)
            self.setOpacity(0.8)
            super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.dragged:
            self.dragged = False
            self.setZValue(0)
            self.setOpacity(1.0)
            
            # 吸附到最近的网格点
            scene = self.scene()
            if scene:
                # 通过视图获取网格大小
                view = scene.views()[0] if scene.views() else None
                if view and hasattr(view, 'parent') and view.parent():
                    game_widget = view.parent()
                    if hasattr(game_widget, 'grid_size'):
                        grid_size = game_widget.grid_size
                        
                        # 计算最近的网格位置
                        x = round(self.pos().x() / grid_size) * grid_size
                        y = round(self.pos().y() / grid_size) * grid_size
                        
                        # 确保不超出图片边界
                        img_width = game_widget.pixmap.width()
                        img_height = game_widget.pixmap.height()
                        x = max(0, min(x, img_width - self.pixmap().width()))
                        y = max(0, min(y, img_height - self.pixmap().height()))
                        
                        # 检查目标位置是否已被占用
                        target_occupied = False
                        target_piece = None
                        for piece in game_widget.puzzle_pieces:
                            if piece != self and piece.pos().x() == x and piece.pos().y() == y:
                                target_occupied = True
                                target_piece = piece
                                break
                        
                        # 如果目标位置未被占用，则移动到该位置
                        if not target_occupied:
                            self.setPos(x, y)
                        # 如果目标位置已被占用，则交换两个拼图块的位置
                        else:
                            # 交换位置
                            current_x, current_y = self.pos().x(), self.pos().y()
                            self.setPos(x, y)
                            target_piece.setPos(current_x, current_y)
                
            super().mouseReleaseEvent(event)

    def mouseMoveEvent(self, event):
        if self.dragged:
            # 限制移动范围在图片区域内
            scene = self.scene()
            if scene:
                view = scene.views()[0] if scene.views() else None
                if view and hasattr(view, 'parent') and view.parent():
                    game_widget = view.parent()
                    if hasattr(game_widget, 'pixmap'):
                        # 获取图片尺寸
                        img_width = game_widget.pixmap.width()
                        img_height = game_widget.pixmap.height()
                        
                        # 获取当前鼠标位置
                        newPos = event.scenePos()
                        
                        # 限制x坐标在图片范围内
                        if newPos.x() < 0:
                            newPos.setX(0)
                        elif newPos.x() > img_width - self.pixmap().width():
                            newPos.setX(img_width - self.pixmap().width())
                            
                        # 限制y坐标在图片范围内
                        if newPos.y() < 0:
                            newPos.setY(0)
                        elif newPos.y() > img_height - self.pixmap().height():
                            newPos.setY(img_height - self.pixmap().height())
                            
                        # 设置新的位置
                        self.setPos(newPos)
                        return
                        
            super().mouseMoveEvent(event)

class GameWidget(QWidget):
    game_completed = pyqtSignal()
    back_to_main = pyqtSignal()
    
    def __init__(self, image_path, parent=None, grid_size=3):
        super().__init__(parent)
        self.image_path = image_path
        self.scene = QGraphicsScene(self)
        self.view = QGraphicsView(self.scene)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.view.setRenderHint(QPainter.SmoothPixmapTransform)
        
        # 设置视图的拖拽模式
        self.view.setDragMode(QGraphicsView.NoDrag)
        
        # 初始化游戏变量
        self.puzzle_pieces = []
        self.grid_size = 100  # 默认网格大小
        # 修改: 使用传入的grid_size参数设置行列数
        self.rows = grid_size
        self.cols = grid_size
        
        # 创建界面
        self.init_ui()
        
        # 加载图片并创建拼图
        if self.image_path and os.path.exists(self.image_path):
            self.load_puzzle_image(self.image_path)
            
    def init_ui(self):
        layout = QVBoxLayout()
        
        # 控制按钮
        button_layout = QHBoxLayout()
        
        self.check_button = QPushButton("检查结果")
        self.check_button.clicked.connect(self.check_solution)
        button_layout.addWidget(self.check_button)
        
        self.restart_button = QPushButton("重新开始")
        self.restart_button.clicked.connect(self.restart_game)
        button_layout.addWidget(self.restart_button)
        
        self.back_button = QPushButton("返回主菜单")
        self.back_button.clicked.connect(self.back_to_main.emit)  # 修改: 确保正确连接信号
        button_layout.addWidget(self.back_button)
        
        layout.addLayout(button_layout)
        layout.addWidget(self.view)
        self.setLayout(layout)
        
    def load_puzzle_image(self, image_path):
        """加载拼图图片"""
        try:
            # 使用OpenCV加载图片以支持更多格式
            image = cv2.imread(image_path)
            if image is None:
                logger.error(f"无法加载图片: {image_path}")
                return
                
            # 转换颜色空间 (BGR to RGB)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            
            # 转换为QImage
            height, width, channel = image.shape
            bytes_per_line = 3 * width
            q_img = QImage(image.data, width, height, bytes_per_line, QImage.Format_RGB888)
            
            # 转换为QPixmap
            self.pixmap = QPixmap.fromImage(q_img)
            
            # 创建拼图块
            self.create_puzzle_pieces()
            
        except Exception as e:
            logger.error(f"加载图片时出错: {str(e)}")
            
    def create_puzzle_pieces(self):
        """创建拼图块"""
        if self.pixmap.isNull():
            return
            
        # 清除现有的拼图块
        for piece in self.puzzle_pieces:
            self.scene.removeItem(piece)
        self.puzzle_pieces.clear()
        
        # 计算拼图块尺寸，使用整数除法避免精度问题
        img_width = self.pixmap.width()
        img_height = self.pixmap.height()
        piece_width = img_width // self.cols
        piece_height = img_height // self.rows
        
        # 设置场景大小为图片大小，而不是固定值
        self.scene.setSceneRect(0, 0, img_width, img_height)
        self.grid_size = min(piece_width, piece_height)
        
        # 创建拼图块列表（所有位置）
        positions = [(i, j) for i in range(self.rows) for j in range(self.cols)]
            
        # 创建打乱顺序的位置列表
        shuffled_positions = positions.copy()
        random.shuffle(shuffled_positions)
        
        # 创建拼图块
        for idx, original_pos in enumerate(positions):
            current_pos = shuffled_positions[idx]
            
            # 计算在图片中的位置，使用整数运算避免精度问题
            x = original_pos[1] * piece_width
            y = original_pos[0] * piece_height
            
            # 确保最后一个拼图块不会超出边界，并且能够填满整个图片
            # 计算实际宽度和高度
            if original_pos[1] == self.cols - 1:  # 最后一列
                # 确保最后一列的拼图块能填满剩余空间
                actual_width = img_width - x
            else:
                actual_width = piece_width
                
            if original_pos[0] == self.rows - 1:  # 最后一行
                # 确保最后一行的拼图块能填满剩余空间
                actual_height = img_height - y
            else:
                actual_height = piece_height
            
            # 裁剪图片的一部分，使用精确的矩形区域
            rect = QRectF(x, y, actual_width, actual_height)
            piece_pixmap = self.pixmap.copy(rect.toRect())
            
            # 创建拼图块对象
            piece = PuzzlePiece(piece_pixmap, current_pos, original_pos)
            
            # 设置位置，使用精确的坐标计算
            display_x = current_pos[1] * piece_width
            display_y = current_pos[0] * piece_height
            piece.setPos(display_x, display_y)
            
            # 添加到场景和列表
            self.scene.addItem(piece)
            self.puzzle_pieces.append(piece)
            
        # 适应视图大小
        self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)

    def check_solution(self):
        """检查解决方案"""
        correct = 0
        total = len(self.puzzle_pieces)
        
        # 获取拼图参数
        img_width = self.pixmap.width()
        img_height = self.pixmap.height()
        piece_width = img_width // self.cols
        piece_height = img_height // self.rows
        
        for piece in self.puzzle_pieces:
            # 检查每个拼图块是否在正确位置
            # 使用整数除法计算当前所在网格位置
            current_x = int(piece.pos().x() / piece_width)
            current_y = int(piece.pos().y() / piece_height)
            
            # 确保不超出边界
            current_x = min(current_x, self.cols - 1)
            current_y = min(current_y, self.rows - 1)
            
            if (current_y, current_x) == piece.original_pos:
                correct += 1
                
        # 如果所有拼图块都在正确位置，游戏完成
        if correct == total:
            self.game_completed.emit()
        else:
            # 添加反馈，让用户知道检查结果
            from PyQt5.QtWidgets import QMessageBox
            QMessageBox.information(self, "检查结果", f"已完成 {correct}/{total} 个拼图块")

    def restart_game(self):
        """重新开始游戏"""
        # 重新打乱拼图块
        if self.puzzle_pieces:
            positions = [(i, j) for i in range(self.rows) for j in range(self.cols)]
                
            shuffled_positions = positions.copy()
            random.shuffle(shuffled_positions)
            
            # 获取拼图参数
            img_width = self.pixmap.width()
            img_height = self.pixmap.height()
            piece_width = img_width // self.cols
            piece_height = img_height // self.rows
            
            # 更新每个拼图块的位置
            for idx, piece in enumerate(self.puzzle_pieces):
                new_pos = shuffled_positions[idx]
                piece.current_pos = new_pos
                # 使用精确的坐标计算位置，确保与create_puzzle_pieces方法中一致
                display_x = new_pos[1] * piece_width
                display_y = new_pos[0] * piece_height
                piece.setPos(display_x, display_y)

    def resizeEvent(self, event):
        """窗口大小改变时调整视图"""
        super().resizeEvent(event)
        if self.scene:
            self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)